/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef NWEB_HANDLER_DELEGATE_H
#define NWEB_HANDLER_DELEGATE_H

#include "cef/include/base/cef_lock.h"
#include "cef/include/cef_client.h"
#include "cef/include/cef_dialog_handler.h"
#include "cef/include/cef_form_handler.h"
#include "cef/include/cef_jsdialog_handler.h"
#include "cef/include/cef_media_handler.h"
#include "cef/include/cef_permission_request.h"
#include "cef/include/cef_resource_request_handler.h"

#include "nweb_event_handler.h"
#include "nweb_handler.h"
#include "nweb_preference_delegate.h"
#include "nweb_render_handler.h"

#include <condition_variable>
#include <functional>
#include <list>
#include <mutex>
#include <string>
#include <unordered_set>
#include "capi/nweb_app_client_extension_callback.h"
#include "nweb_download_callback.h"
#include "nweb_javascript_result_callback.h"
#include "nweb_value.h"

struct NativeWindow;

namespace OHOS::NWeb {
class NWebHandler;
class NWebGeolocationCallback;
class NWebFindDelegate;

class NWebHandlerDelegate : public CefClient,
                            public CefLifeSpanHandler,
                            public CefLoadHandler,
                            public CefRequestHandler,
                            public CefResourceRequestHandler,
                            public CefDisplayHandler,
                            public CefDownloadHandler,
                            public CefFocusHandler,
                            public CefPermissionRequest,
                            public CefJSDialogHandler,
                            public CefDialogHandler,
                            public CefContextMenuHandler,
                            public CefFindHandler,
                            public CefKeyboardHandler,
                            public CefMediaHandler,
                            public CefCookieAccessFilter,
                            public CefPrintHandler,
                            public CefFormHandler {
 public:
  static CefRefPtr<NWebHandlerDelegate> Create(
      std::shared_ptr<NWebPreferenceDelegate> preference_delegate,
      CefRefPtr<NWebRenderHandler> render_handler,
      std::shared_ptr<NWebEventHandler> event_handler,
      std::shared_ptr<NWebFindDelegate> find_delegate,
      bool is_enhance_surface,
      void* window);

  NWebHandlerDelegate(
      std::shared_ptr<NWebPreferenceDelegate> preference_delegate,
      CefRefPtr<NWebRenderHandler> render_handler,
      std::shared_ptr<NWebEventHandler> event_handler,
      std::shared_ptr<NWebFindDelegate> find_delegate,
      bool is_enhance_surface,
      void* window);
  ~NWebHandlerDelegate() = default;

  void OnDestroy();

  void RegisterDownLoadListener(
      std::shared_ptr<NWebDownloadCallback> download_listener);
  void RegisterReleaseSurfaceListener(
     std::shared_ptr<NWebReleaseSurfaceCallback> releaseSurfaceListener);
  void RegisterNWebHandler(std::shared_ptr<NWebHandler> handler);
  void RegisterWebAppClientExtensionListener(
      std::shared_ptr<NWebAppClientExtensionCallback>
          web_app_client_extension_listener);
  void UnRegisterWebAppClientExtensionListener();
  void RegisterNWebJavaScriptCallBack(
      std::shared_ptr<NWebJavaScriptResultCallBack> callback);

  // Request that all existing browser windows close.
  void CloseAllBrowsers(bool force_close);
  bool IsClosing() const;
  const CefRefPtr<CefBrowser> GetBrowser();

  /* CefClient methods begin */
  CefRefPtr<CefDownloadHandler> GetDownloadHandler() override;
  CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override;
  CefRefPtr<CefLoadHandler> GetLoadHandler() override;
  CefRefPtr<CefRenderHandler> GetRenderHandler() override;
  CefRefPtr<CefRequestHandler> GetRequestHandler() override;
  CefRefPtr<CefDisplayHandler> GetDisplayHandler() override;
  CefRefPtr<CefFocusHandler> GetFocusHandler() override;
  CefRefPtr<CefPermissionRequest> GetPermissionRequest() override;
  CefRefPtr<CefJSDialogHandler> GetJSDialogHandler() override;
  CefRefPtr<CefDialogHandler> GetDialogHandler() override;
  CefRefPtr<CefContextMenuHandler> GetContextMenuHandler() override;
  CefRefPtr<CefMediaHandler> GetMediaHandler() override;
  CefRefPtr<CefCookieAccessFilter> GetCookieAccessFilter(
                            CefRefPtr<CefBrowser> browser,
                            CefRefPtr<CefFrame> frame,
                            CefRefPtr<CefRequest> request) override;
  virtual bool OnProcessMessageReceived(
      CefRefPtr<CefBrowser> browser,
      CefRefPtr<CefFrame> frame,
      CefProcessId source_process,
      CefRefPtr<CefProcessMessage> message) override;
  int NotifyJavaScriptResult(CefRefPtr<CefListValue> args,
                             const CefString& method,
                             const CefString& object_name,
                             CefRefPtr<CefListValue> result,
                             int32_t routing_id,
                             int32_t object_id) override;
  bool HasJavaScriptObjectMethods(int32_t object_id,
                                  const CefString& method_name) override;
  void GetJavaScriptObjectMethods(
      int32_t object_id,
      CefRefPtr<CefValue> returned_method_names) override;
  void RemoveJavaScriptObjectHolder(int32_t holder, int32_t object_id) override;
  void RemoveTransientJavaScriptObject() override;

  CefRefPtr<CefFormHandler> GetFormHandler() override;
  // #if defined(OHOS_NWEB_EX)
  void ShowPasswordDialog(bool is_update, const CefString& url) override;
  void OnShowAutofillPopup(
      CefRefPtr<CefBrowser> browser,
      const CefRect& bounds,
      bool right_aligned,
      const std::vector<CefAutofillPopupItem>& menu_items) override;
  void OnHideAutofillPopup() override;
  // #endif

  CefRefPtr<CefFindHandler> GetFindHandler() override;
  CefRefPtr<CefKeyboardHandler> GetKeyboardHandler() override;
  CefRefPtr<CefPrintHandler> GetPrintHandler() override;
  /* CefClient methods end */

  /* CefLifeSpanHandler methods begin */
  void OnAfterCreated(CefRefPtr<CefBrowser> browser) override;
  bool DoClose(CefRefPtr<CefBrowser> browser) override;
  void OnBeforeClose(CefRefPtr<CefBrowser> browser) override;
  bool OnBeforePopup(
      CefRefPtr<CefBrowser> browser,
      CefRefPtr<CefFrame> frame,
      const CefString& target_url,
      const CefString& target_frame_name,
      CefLifeSpanHandler::WindowOpenDisposition target_disposition,
      bool user_gesture,
      const CefPopupFeatures& popup_features,
      CefWindowInfo& window_info,
      CefRefPtr<CefClient>& client,
      CefBrowserSettings& settings,
      CefRefPtr<CefDictionaryValue>& extra_info,
      bool* no_javascript_access) override;
  bool OnPreBeforePopup(CefRefPtr<CefBrowser> browser,
                        CefRefPtr<CefFrame> frame,
                        const CefString& target_url,
                        CefLifeSpanHandler::WindowOpenDisposition target_disposition,
                        bool user_gesture,
                        CefRefPtr<CefCallback> callback) override;
  /* CefLifeSpanHandler methods end */

  /* CefLoadHandler methods begin */
  void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,
                            bool is_loading,
                            bool can_go_back,
                            bool can_go_forward) override;

  void OnLoadStart(CefRefPtr<CefBrowser> browser,
                   CefRefPtr<CefFrame> frame,
                   const CefString& url,
                   TransitionType transition_type) override;

  void OnLoadEnd(CefRefPtr<CefBrowser> browser,
                 CefRefPtr<CefFrame> frame,
                 int http_status_code) override;

  void OnLoadError(CefRefPtr<CefBrowser> browser,
                   CefRefPtr<CefFrame> frame,
                   ErrorCode error_code,
                   const CefString& error_text,
                   const CefString& failed_url) override;

  void OnLoadErrorWithRequest(CefRefPtr<CefRequest> request,
                              bool is_main_frame,
                              bool has_user_gesture,
                              int error_code,
                              const CefString& error_text) override;

  void OnHttpError(CefRefPtr<CefRequest> request,
                   bool is_main_frame,
                   bool has_user_gesture,
                   CefRefPtr<CefResponse> response) override;

  void OnRefreshAccessedHistory(CefRefPtr<CefBrowser> browser,
                                CefRefPtr<CefFrame> frame,
                                const CefString& url,
                                bool isReload) override;

  void OnPageVisible(CefRefPtr<CefBrowser> browser,
                     const CefString& url,
                     bool success) override;

  void OnFirstContentfulPaint(int64_t navigationStartTick,
                              int64_t firstContentfulPaintMs) override;

  void OnDataResubmission(CefRefPtr<CefBrowser> browser,
                          CefRefPtr<CefCallback> callback) override;
  /* CefLoadHandler methods end */

  /* CefRequestHandler methods begin */
  bool OnBeforeBrowse(CefRefPtr<CefBrowser> browser,
                      CefRefPtr<CefFrame> frame,
                      CefRefPtr<CefRequest> request,
                      bool user_gesture,
                      bool is_redirect) override;
  bool OnCertificateError(CefRefPtr<CefBrowser> browser,
                          cef_errorcode_t cert_error,
                          const CefString& request_url,
                          CefRefPtr<CefSSLInfo> ssl_info,
                          CefRefPtr<CefCallback> callback) override;

  bool OnSelectClientCertificate(
      CefRefPtr<CefBrowser> browser,
      bool isProxy,
      const CefString& host,
      int port,
      const std::vector<CefString>& key_types,
      const std::vector<CefString>& principals,
      const X509CertificateList& certificates,
      CefRefPtr<CefSelectClientCertificateCallback> callback) override;

  CefRefPtr<CefResourceRequestHandler> GetResourceRequestHandler(
      CefRefPtr<CefBrowser> browser,
      CefRefPtr<CefFrame> frame,
      CefRefPtr<CefRequest> request,
      bool is_navigation,
      bool is_download,
      const CefString& request_initiator,
      bool& disable_default_handling) override;

  void OnRenderProcessTerminated(CefRefPtr<CefBrowser> browser,
                                 TerminationStatus status) override;

  bool GetAuthCredentials(CefRefPtr<CefBrowser> browser,
                          const CefString& origin_url,
                          bool isProxy,
                          const CefString& host,
                          int port,
                          const CefString& realm,
                          const CefString& scheme,
                          CefRefPtr<CefAuthCallback> callback) override;
  /* CefRequestHandler methods end */

  /* CefDownloadHandler methods begin */
  void OnBeforeDownload(CefRefPtr<CefBrowser> browser,
                        CefRefPtr<CefDownloadItem> download_item,
                        const CefString& suggested_name,
                        CefRefPtr<CefBeforeDownloadCallback> callback) override;
  /* CefDownloadHandler methods end */

  /* CefKeyboardHandler methods begin */
  bool OnPreKeyEvent(CefRefPtr<CefBrowser> browser,
                     const CefKeyEvent& event,
                     CefEventHandle os_event,
                     bool* is_keyboard_shortcut) override;

  bool OnKeyEvent(CefRefPtr<CefBrowser> browser,
                  const CefKeyEvent& event,
                  CefEventHandle os_event) override;
  /* CefKeyboardHandler methods begin */

  /* CefResourceRequestHandler method begin */
  ReturnValue OnBeforeResourceLoad(
      CefRefPtr<CefBrowser> browser,
      CefRefPtr<CefFrame> frame,
      CefRefPtr<CefRequest> request,
      CefRefPtr<CefCallback> callback) override;

  CefRefPtr<CefResourceHandler> GetResourceHandler(
      CefRefPtr<CefBrowser> browser,
      CefRefPtr<CefFrame> frame,
      CefRefPtr<CefRequest> request) override;
  /* CefResourceRequestHandler method end */

  /* CefDisplayHandler method begin */
  void OnTitleChange(CefRefPtr<CefBrowser> browser,
                     const CefString& title) override;
  void OnLoadingProgressChange(CefRefPtr<CefBrowser> browser,
                               double progress) override;
  void OnFullscreenModeChange(CefRefPtr<CefBrowser> browser,
                              bool full_screen) override;
  void OnReceivedIcon(const void* data,
                      size_t width,
                      size_t height,
                      cef_color_type_t color_type,
                      cef_alpha_type_t alpha_type) override;
  void OnReceivedIconUrl(const CefString& image_url,
                         const void* data,
                         size_t width,
                         size_t height,
                         cef_color_type_t color_type,
                         cef_alpha_type_t alpha_type) override;
  void OnReceivedTouchIconUrl(CefRefPtr<CefBrowser> browser,
                              const CefString& icon_url,
                              bool precomposed) override;
  bool OnConsoleMessage(CefRefPtr<CefBrowser> browser,
                        cef_log_severity_t level,
                        const CefString& message,
                        const CefString& source,
                        int line) override;
  void OnScaleChanged(CefRefPtr<CefBrowser> browser,
                      float old_page_scale_factor,
                      float new_page_scale_factor) override;
  bool OnCursorChange(CefRefPtr<CefBrowser> browser,
                      CefCursorHandle cursor,
                      cef_cursor_type_t type,
                      const CefCursorInfo& custom_cursor_info) override;
  // #ifdef OHOS_NWEB_EX
  void OnContentsBrowserZoomChange(double zoom_factor,
                                   bool can_show_bubble) override;
  // #endif
  
  /* CefDisplayHandler method end */

  /* CefFocusHandler method begin */
  bool OnSetFocus(CefRefPtr<CefBrowser> browser, FocusSource source) override;
  /* CefFocusHandler method end */

  /* CefFormHandler method begin */
  void OnFormEditingStateChanged(CefRefPtr<CefBrowser> browser, bool is_editing, uint64_t form_id) override;
  /* CefFormHandler method end */

  /* CefPermissionRequest method begin */
  void OnGeolocationShow(const CefString& origin) override;

  void OnGeolocationHide() override;

  void OnPermissionRequest(CefRefPtr<CefAccessRequest> request) override;
  void OnPermissionRequestCanceled(
      CefRefPtr<CefAccessRequest> request) override;

  void OnScreenCaptureRequest(CefRefPtr<CefScreenCaptureAccessRequest> request) override;
  /* CefPermissionRequest method end */

  /* CefJSDialogHandler method begin */
  bool OnJSDialog(CefRefPtr<CefBrowser> browser,
                  const CefString& origin_url,
                  JSDialogType dialog_type,
                  const CefString& message_text,
                  const CefString& default_prompt_text,
                  CefRefPtr<CefJSDialogCallback> callback,
                  bool& suppress_message) override;

  bool OnBeforeUnloadDialog(CefRefPtr<CefBrowser> browser,
                            const CefString& url,
                            const CefString& message_text,
                            bool is_reload,
                            CefRefPtr<CefJSDialogCallback> callback) override;
  /* CefJSDialogHandler method end */

  /* CefDialogHandler method begin */
  bool OnFileDialog(CefRefPtr<CefBrowser> browser,
                    FileDialogMode mode,
                    const CefString& title,
                    const CefString& default_file_path,
                    const std::vector<CefString>& accept_filters,
                    int selected_accept_filter,
                    bool capture,
                    CefRefPtr<CefFileDialogCallback> callback) override;
  void OnSelectPopupMenu(CefRefPtr<CefBrowser> browser,
                         const CefRect& bounds,
                         int item_height,
                         double item_font_size,
                         int selected_item,
                         const std::vector<CefSelectPopupItem>& menu_items,
                         bool right_aligned,
                         bool allow_multiple_selection,
                         CefRefPtr<CefSelectPopupCallback> callback) override;
  void OnDateTimeChooserPopup(CefRefPtr<CefBrowser> browser,
                              const CefDateTimeChooser& date_time_chooser,
                              const std::vector<CefDateTimeSuggestion>& suggestion,
                              CefRefPtr<CefDateTimeChooserCallback> callback) override;
  void OnDateTimeChooserClose() override;
  /* CefDialogHandler method end */

  /* CefContextMenuHandler method begin */
  void OnBeforeContextMenu(CefRefPtr<CefBrowser> browser,
                           CefRefPtr<CefFrame> frame,
                           CefRefPtr<CefContextMenuParams> params,
                           CefRefPtr<CefMenuModel> model) override;
  bool RunContextMenu(CefRefPtr<CefBrowser> browser,
                      CefRefPtr<CefFrame> frame,
                      CefRefPtr<CefContextMenuParams> params,
                      CefRefPtr<CefMenuModel> model,
                      CefRefPtr<CefRunContextMenuCallback> callback) override;
  void OnGetImageForContextNode(CefRefPtr<CefBrowser> browser,
                                CefRefPtr<CefImage> image) override;
  void OnGetImageFromCache(CefRefPtr<CefImage> image) override;
  bool OnContextMenuCommand(CefRefPtr<CefBrowser> browser,
                            CefRefPtr<CefFrame> frame,
                            CefRefPtr<CefContextMenuParams> params,
                            int command_id,
                            CefContextMenuHandler::EventFlags event_flags) override;
  void OnContextMenuDismissed(CefRefPtr<CefBrowser> browser,
                              CefRefPtr<CefFrame> frame) override;
  bool RunQuickMenu(CefRefPtr<CefBrowser> browser,
                    CefRefPtr<CefFrame> frame,
                    const CefPoint& location,
                    const CefSize& size,
                    CefContextMenuHandler::QuickMenuEditStateFlags edit_state_flags,
                    CefRefPtr<CefRunQuickMenuCallback> callback) override;
  bool OnQuickMenuCommand(CefRefPtr<CefBrowser> browser,
                          CefRefPtr<CefFrame> frame,
                          int command_id,
                          CefContextMenuHandler::EventFlags event_flags) override;
  void OnQuickMenuDismissed(CefRefPtr<CefBrowser> browser,
                            CefRefPtr<CefFrame> frame) override;
  /* CefContextMenuHandler method end */

  /* CefFindandler methods begin */
  void OnFindResult(CefRefPtr<CefBrowser> browser,
                    int identifier,
                    int count,
                    const CefRect& selectionRect,
                    int activeMatchOrdinal,
                    bool finalUpdate) override;
  /* CefFindandler methods end */

  /* CefResourceRequestHandler methods begin */
  bool CanSendCookie(CefRefPtr<CefBrowser> browser,
                            CefRefPtr<CefFrame> frame,
                            CefRefPtr<CefRequest> request,
                            const CefCookie& cookie) override;
  bool CanSaveCookie(CefRefPtr<CefBrowser> browser,
                            CefRefPtr<CefFrame> frame,
                            CefRefPtr<CefRequest> request,
                            CefRefPtr<CefResponse> response,
                            const CefCookie& cookie) override;
  /* CefResourceRequestHandler methods end */

  /* CefMediaHandler methods begin */
  void OnAudioStateChanged(CefRefPtr<CefBrowser> browser,
                           bool audible) override;
  void OnMediaStateChanged(CefRefPtr<CefBrowser> browser,
                           MediaType type,
                           MediaPlayingState state) override;
  /* CefMediaHandler methods end */
  /* CefPrintHandler method begin */
  void OnPrintStart(CefRefPtr<CefBrowser> browser) override;
  void OnPrintSettings(CefRefPtr<CefBrowser> browser,
                      CefRefPtr<CefPrintSettings> settings,
                       bool get_defaults) override;
  bool OnPrintDialog(CefRefPtr<CefBrowser> browser,
                     bool has_selection,
                     CefRefPtr<CefPrintDialogCallback> callback) override;
  bool OnPrintJob(CefRefPtr<CefBrowser> browser,
                  const CefString& document_name,
                  const CefString& pdf_file_path,
                  CefRefPtr<CefPrintJobCallback> callback) override;
  void OnPrintReset(CefRefPtr<CefBrowser> browser) override;
  CefSize GetPdfPaperSize(CefRefPtr<CefBrowser> browser,
                          int device_units_per_inch) override;
  /* CefPrintHandler method end */

  const std::vector<std::string> GetVisitedHistory();

#if defined(REPORT_SYS_EVENT)
  void SetNWebId(uint32_t nwebId);
  uint32_t GetNWebId();
#endif
  void SetWindowId(uint32_t window_id) { window_id_ = window_id; };

  void SetFavicon(const void* icon_data, size_t width, size_t height,
    ImageColorType color_type, ImageAlphaType alpha_type);
  bool GetFavicon(const void** data, size_t& width, size_t& height,
    ImageColorType& colorType, ImageAlphaType& alphaType);
  float GetScale() const { return scale_; }

  bool GetFocusState();
  void SetFocusState(bool focusState);
  bool GetContinueNeedFocus();
  void SetContinueNeedFocus(bool continueNeedFocus);

  void NotifyPopupWindowResult(bool result);

  // #if defined(OHOS_NWEB_EX)
  void OnTopControlsChanged(float top_controls_offset,
                            float top_content_offset) override;
  int OnGetTopControlsHeight() override;
  bool DoBrowserControlsShrinkRendererSize() override;
  // #endif

  // save ark js function for window.open 
  void SavaArkJSFunctionForPopup(const std::string& object_name,
                                 const std::vector<std::string>& method_list,
                                 const int32_t object_id);
 private:
  void CopyImageToClipboard(CefRefPtr<CefImage> image);
  // List of existing browser windows. Only accessed on the CEF UI thread.
  typedef std::list<CefRefPtr<CefBrowser>> BrowserList;
  BrowserList browser_list_;

  CefRefPtr<CefBrowser> main_browser_ = nullptr;
  bool is_closing_ = false;

  std::shared_ptr<NWebPreferenceDelegate> preference_delegate_ = nullptr;
  CefRefPtr<NWebRenderHandler> render_handler_ = nullptr;

  std::shared_ptr<NWebEventHandler> event_handler_ = nullptr;

  // Include the default reference counting implementation.
  IMPLEMENT_REFCOUNTING(NWebHandlerDelegate);

  std::shared_ptr<NWebDownloadCallback> download_listener_ = nullptr;
  std::shared_ptr<NWebReleaseSurfaceCallback> releaseSurfaceListener_ = nullptr;
  std::shared_ptr<NWebHandler> nweb_handler_ = nullptr;
  std::shared_ptr<NWebJavaScriptResultCallBack> nweb_javascript_callback_ =
      nullptr;
  std::shared_ptr<NWebFindDelegate> find_delegate_ = nullptr;
  std::shared_ptr<NWebAppClientExtensionCallback>
      web_app_client_extension_listener_ = nullptr;

  std::shared_ptr<NWebGeolocationCallback> callback_ = nullptr;
  bool is_enhance_surface_ = false;
  void* window_ = nullptr;

  CefString image_cache_src_url_;

  // the received icon
  base::Lock state_lock_;
  const void* data_ = nullptr;
  size_t width_ = 0;
  size_t height_ = 0;
  ImageColorType color_type_ = ImageColorType::COLOR_TYPE_UNKNOWN;
  ImageAlphaType alpha_type_ = ImageAlphaType::ALPHA_TYPE_UNKNOWN;

#if defined(REPORT_SYS_EVENT)
  uint32_t nweb_id_ = 0;
  // For page load statistics
  uint32_t access_sum_count_ = 0;
  uint32_t access_success_count_ = 0;
  uint32_t access_fail_count_ = 0;
#endif
  uint32_t window_id_ = 0;

  static int32_t popIndex_;
  CefRefPtr<CefCallback> popupWindowCallback_ = nullptr;

#if defined(OHOS_NWEB_EX)
  bool on_load_start_notified_ = false;
  float top_content_offset_ = 0;
#endif  // OHOS_NWEB_EX

  float scale_ = 100.0;
  bool focusState_ = false;
  bool continueNeedFocus_ = false;

#if BUILDFLAG(IS_OHOS)
  std::vector<uint64_t> edited_forms_id_;
#endif

  // js property name and object id
  using MethodPair = std::pair<std::string, std::unordered_set<std::string>>;
  using ObjectMethodMap = std::map<int32_t, MethodPair>;
  ObjectMethodMap javascript_method_map_;
};
}  // namespace OHOS::NWeb

#endif
